home *** CD-ROM | disk | FTP | other *** search
- /*
- * This software is copyright 1992 by Robert Morris.
- * You may freely redistribute this software as shareware
- * if you do so in the same form as you got it. If you find
- * this software useful, please send $12 to:
- * Robert Morris
- * P.O. Box 1044
- * Harvard Square Station
- * Cambridge, MA 02238
- * ecognome@aol.com
- * If you incorporate any of this software in any kind of
- * commercial product, please send $2 per copy distributed
- * to the above address.
- */
-
- /*
- * utilities to help parse RTF.
- */
-
- #include <stdlib.h>
- #include <ctype.h>
- #include <string.h>
- #include <HyperXCmd.h>
- #include "lists.h"
- #include "rtf.h"
-
- int sprintf(char *, ...);
-
- extern int goterror;
- void error(char *);
-
- typedef struct state{
- int font;
- int face;
- int size;
- int dirty; /* do we need a new TE style before next char? */
- int isfonttable; /* \fonttbl group */
- int deffont; /* default rtf font # */
- int invisible;
- struct state *last;
- } State;
- State *pushstate(State *old), *popstate(State *);
-
- struct ftable{
- int rtf; /* rtf font # */
- int mac; /* mac font # */
- } **ftable;
-
- int startstyle(MyStyleHandle sh, long start, int font, int face, int size);
- void readcontrol(struct list *, char *word, char *arg);
- void docontrol(struct list *inp, struct list *outp, State *state,
- MyStyleHandle sh, char control[], char arg[]);
- void appchars(struct list *outp, char *s, State *state, MyStyleHandle sh);
-
- /*
- * txt should be a handle to RTF text.
- * puts a (partial) style handle into *outsh, and a text handle into *outtxt.
- * if outsh is zero, don't bother with styles.
- * the text handle is null-terminated.
- */
- void
- parsertf(Handle txt, MyStyleHandle *outsh, Handle *outtxt)
- {
- struct list out, in;
- register int c;
- STElement *stp;
- MyStyleHandle sh;
- register State *state;
-
- if(outsh)
- *outsh = 0;
- *outtxt = 0;
- out.h = 0;
- state = 0;
- sh = 0;
- ftable = 0;
-
- ftable = (struct ftable **)NewHandle(0L);
- if(ftable == 0){
- error("error : out of memory for ftable");
- goto out;
- }
-
- if(outsh){
- sh = NewMyStyleHandle();
- if(sh == 0){
- error("error : out of memory for sh");
- goto out;
- }
- } else {
- sh = 0;
- }
-
- state = pushstate((State *) 0);
- if(state == 0){
- error("error : out of memory for state");
- goto out;
- }
-
- NewList(txt, &in);
- NewList((char **) 0, &out);
-
- while((c = ReadListChar(&in)) != -1 && c != '\0' && goterror == 0){
- if(c == '\\'){
- char control[256], arg[256];
- readcontrol(&in, control, arg);
- docontrol(&in, &out, state, sh, control, arg);
- } else if(c == '{'){
- state = pushstate(state);
- if(state == 0){
- error("error : out of memory for new state");
- goto out;
- }
- } else if(c == '}'){
- state = popstate(state);
- if(state == 0){
- error("error : unbalanced }");
- goto out;
- }
- state->dirty = 1;
- } else if(state->isfonttable == 0 &&
- c != '\r' &&
- c != '\n' &&
- state->invisible == 0){
- if(state->dirty){
- startstyle(sh, out.ptr, state->font, state->face, state->size);
- state->dirty = 0;
- }
- _AppendListChar(&out, c);
- }
- }
-
- /* add dummy style to mark the end */
- startstyle(sh, out.ptr, 0, 0, 12);
-
- if(sh)
- (*sh)->nRuns -= 1; /* nRuns doesn't count that last dummy run! */
-
- if(goterror)
- goto out;
-
- TrimList(&out);
- if(out.h == 0){
- error("error : out of memory for output text");
- goto out;
- }
-
- *outtxt = out.h;
- out.h = 0;
- if(outsh){
- *outsh = sh;
- sh = 0;
- }
-
- out:
- if(ftable)
- DisposHandle((Handle) ftable);
- if(sh)
- DisposMyStyleHandle(sh);
- if(out.h)
- DisposHandle(out.h);
- while(state)
- state = popstate(state);
- }
-
- int
- startstyle(sh, start, font, face, size)
- register MyStyleHandle sh;
- long start;
- int font, face, size;
- {
- register int si;
- int run, err;
- register STPtr st;
- STPtr st1;
- long newlen;
-
- if(sh == 0)
- return;
-
- /* is there an existing style like this? */
- st = 0;
- for(si = 0; si < (*sh)->nStyles; si++){
- st = (*((*sh)->styleTab)) + si;
- if(st->stFont == font && st->stFace == face && st->stSize == size)
- break;
- }
- if(si >= (*sh)->nStyles)
- st = 0;
-
- /* is the last style just like this one? */
- if(st && (*sh)->nRuns > 0 &&
- (*sh)->runs[(*sh)->nRuns-1].styleIndex == si)
- return;
-
- if(st == 0){
- (*sh)->nStyles += 1;
- SetHandleSize((Handle)((*sh)->styleTab),
- (*sh)->nStyles * sizeof(STElement));
- if((err = MemError()) != 0){
- (*sh)->nStyles = 0;
- SetHandleSize((Handle)((*sh)->styleTab), 0L);
- error("error : out of memory for more styleTab");
- return(err);
- }
- si = (*sh)->nStyles - 1;
- st = (*((*sh)->styleTab)) + si;
- st->stCount = 0;
- st->stHeight = 12; /* ??? */
- st->stAscent = 10; /* ??? */
- st->stFont = font;
- st->stFace = face;
- st->stSize = size;
- st->stColor.red = st->stColor.green = st->stColor.blue = 0;
- }
-
- st->stCount += 1;
-
- if((*sh)->nRuns > 0 && (*sh)->runs[(*sh)->nRuns-1].startChar == start){
- /* overwrite previous run, since it starts in the same place */
- run = (*sh)->nRuns - 1;
- st1 = (*((*sh)->styleTab)) + (*sh)->runs[run].styleIndex;
- st1->stCount -= 1;
- } else {
- run = (*sh)->nRuns;
- (*sh)->nRuns += 1;
- newlen = (char *) &((*sh)->runs[(*sh)->nRuns]) - (char *) *sh;
- SetHandleSize((Handle)sh, newlen);
- if((err = MemError()) != 0){
- (*sh)->nRuns -= 1;
- error("error : out of memory for more runs");
- return(err);
- }
- }
-
- (*sh)->runs[run].startChar = start;
- (*sh)->runs[run].styleIndex = si;
-
- return(0);
- }
-
- /*
- * a '\\' has been read; read the control word and any argument.
- * if the following delimiter is a space, consume it; otherwise
- * leave it alone.
- */
- void
- readcontrol(lp, word, arg)
- struct list *lp;
- char word[], arg[]; /* out parameters: the control word and argument */
- {
- int i, c;
-
- word[0] = arg[0] = '\0';
-
- i = 0;
- while(i < 254 && isalpha(c = ReadListChar(lp)))
- word[i++] = c;
- word[i] = '\0';
-
- if(i == 0){
- switch(c){
- case '\'':
- /* \' followed by two hex digits */
- word[0] = '\'';
- word[1] = '\0';
- arg[0] = ReadListChar(lp);
- arg[1] = ReadListChar(lp);
- arg[2] = '\0';
- return;
- default:
- /* some one-character punctuation code */
- word[0] = c;
- word[1] = '\0';
- return;
- }
- }
-
- if(isdigit(c) || c == '-'){
- /* we have a parameter */
- i = 0;
- do{
- arg[i++] = c;
- } while(i < 254 && isdigit(c = ReadListChar(lp)));
- arg[i] = '\0';
- }
-
- if(c != ' ' && c != '\n' && c != '\r'){
- /* give back the look-ahead character */
- lp->ptr -= 1;
- }
- }
-
- State *
- pushstate(old)
- State *old;
- {
- State *new;
-
- new = (State *) NewPtr(sizeof(State));
- if(new == 0){
- while(old){
- new = old->last;
- DisposPtr((Ptr)old);
- old = new;
- }
- return(0);
- }
-
- if(old){
- *new = *old;
- } else {
- new->font = new->face = 0;
- new->size = 12;
- new->dirty = 1;
- new->isfonttable = 0;
- new->invisible = 0;
- new->deffont = -1;
- }
-
- new->last = old;
- return(new);
- }
-
- State *
- popstate(st)
- State *st;
- {
- State *last;
-
- if(st){
- last = st->last;
- DisposPtr((Ptr)st);
- return(last);
- }
- return(0);
- }
-
- void
- docontrol(inp, outp, state, sh, control, arg)
- struct list *inp, *outp;
- State *state;
- MyStyleHandle sh;
- char control[], arg[];
- {
- int c;
-
- if(strcmp(control, "fonttbl") == 0){
- state->isfonttable = 1;
- SetHandleSize((Handle)ftable, 0L);
- } else if(strcmp(control, "f") == 0 && state->isfonttable){
- int mac, rtf, i, nfonts;
- char family[256], name[256];
-
- rtf = atoi(arg);
-
- c = ReadListChar(inp);
- while(isspace(c))
- c = ReadListChar(inp);
- if(c == '\\'){
- readcontrol(inp, family, name); /* read the family */
- } else {
- inp->ptr -= 1;
- family[0] = '\0';
- }
-
- /* read the font name, up to a semicolon */
- i = 0;
- while(i < 254 && (c = ReadListChar(inp)) != -1 && c != 0 &&
- c != ';' && c != '}' && c != '\\'){
- if(i == 0 && isspace(c))
- continue;
- name[i++] = c;
- }
- name[i] = '\0';
- CtoPstr(name);
- GetFNum((StringPtr)name, &mac);
- nfonts = GetHandleSize((Handle) ftable) / sizeof(**ftable);
- SetHandleSize((Handle) ftable, sizeof(**ftable) * (nfonts + 1));
- if(MemError() == 0){
- (*ftable)[nfonts].rtf = rtf;
- (*ftable)[nfonts].mac = mac;
- } else
- error("error : out of memory for more ftable");
- } else if(strcmp(control, "f") == 0){
- state->font = findrtffont(atoi(arg));
- state->dirty = 1;
- } else if(strcmp(control, "deff") == 0){
- state->deffont = atoi(arg);
- } else if(strcmp(control, "plain") == 0){
- state->font = findrtffont(state->deffont);
- state->face = 0;
- state->size = 12; /* ??? */
- state->dirty = 1;
- state->invisible = 0;
- } else if(strcmp(control, "b") == 0){
- xface(state, bold, arg);
- } else if(strcmp(control, "i") == 0){
- xface(state, italic, arg);
- } else if(strcmp(control, "outl") == 0){
- xface(state, outline, arg);
- } else if(strcmp(control, "shad") == 0){
- xface(state, shadow, arg);
- } else if(strcmp(control, "hcgroup") == 0){
- xface(state, 0x80, arg); /* HyperCard grouped text */
- } else if(strcmp(control, "v") == 0){
- if(arg[0] == '0')
- state->invisible = 0;
- else
- state->invisible = 1;
- } else if(strcmp(control, "fs") == 0){
- state->size = atoi(arg) / 2;
- state->dirty = 1;
- } else if(strcmp(control, "ul") == 0 || strcmp(control, "ulw") == 0 ||
- strcmp(control, "uld") == 0 || strcmp(control, "uldb") == 0){
- xface(state, underline, arg);
- } else if(strcmp(control, "ulnone") == 0){
- state->face &= ~underline;
- state->dirty = 1;
- } else if(strcmp(control, "~") == 0){
- appchars(outp, "\xCA", state, sh); /* option-space */
- } else if(strcmp(control, "_") == 0){
- appchars(outp, "-", state, sh);
- } else if(strcmp(control, "bullet") == 0){
- appchars(outp, "Ñ", state, sh); /* option-8 */
- } else if(strcmp(control, "ldblquote") == 0){
- appchars(outp, "╥", state, sh); /* option-[ */
- } else if(strcmp(control, "rdblquote") == 0){
- appchars(outp, "╙", state, sh); /* option-shift-[ */
- } else if(strcmp(control, "endash") == 0){
- appchars(outp, "╨", state, sh); /* option-- */
- } else if(strcmp(control, "emdash") == 0){
- appchars(outp, "╤", state, sh); /* option-shift-- */
- } else if(strcmp(control, "lquote") == 0){
- appchars(outp, "╘", state, sh); /* option-] */
- } else if(strcmp(control, "rquote") == 0){
- appchars(outp, "╒", state, sh); /* option-shift-] */
- } else if(strcmp(control, "'") == 0){
- char bf[2];
- sscanf(arg, "%x", &c);
- bf[0] = c;
- bf[1] = '\0';
- appchars(outp, bf, state, sh);
- } else if(strcmp(control, "line") == 0){
- appchars(outp, "\r", state, sh);
- } else if(strcmp(control, "tab") == 0){
- appchars(outp, " ", state, sh);
- } else if(strcmp(control, "colortbl") == 0 ||
- strcmp(control, "pict") == 0 ||
- strcmp(control, "footnote") == 0 ||
- strcmp(control, "annotation") == 0 ||
- strcmp(control, "header") == 0 ||
- strcmp(control, "footer") == 0 ||
- strcmp(control, "info") == 0 ||
- strcmp(control, "field") == 0 ||
- strcmp(control, "*") == 0){
- flushgroup(inp);
- } else if(strcmp(control, "stylesheet") == 0){
- flushgroup(inp); /* seems to be OK to ignore */
- } else if(strcmp(control, "par") == 0){
- appchars(outp, "\r", state, sh);
- } else if(isalnum(control[0]) == 0){
- appchars(outp, control, state, sh);
- }
- }
-
- void
- appchars(outp, s, state, sh)
- struct list *outp;
- char *s;
- State *state;
- MyStyleHandle sh;
- {
- if(state->invisible == 0){
- if(state->dirty){
- startstyle(sh, outp->ptr, state->font, state->face, state->size);
- state->dirty = 0;
- }
- AppendList(outp, s);
- }
- }
-
-
- int
- findrtffont(rtf)
- {
- int i, nfonts;
-
- nfonts = GetHandleSize((Handle) ftable) / sizeof(**ftable);
- for(i = 0; i < nfonts; i++){
- if((*ftable)[i].rtf == rtf){
- return((*ftable)[i].mac);
- }
- }
- return(0);
- }
-
- xface(state, face, arg)
- State *state;
- int face;
- char arg[];
- {
- if(arg[0] == '0')
- state->face &= ~face;
- else
- state->face |= face;
- state->dirty = 1;
- }
-
- flushgroup(inp)
- struct list *inp;
- {
- int c, depth;
-
- depth = 0;
- while((c = ReadListChar(inp)) != -1 && c != 0){
- if(c == '}'){
- if(depth <= 0){
- inp->ptr -= 1; /* allow } to pop the state */
- break;
- }
- --depth;
- } else if(c == '{'){
- depth++;
- }
- }
- }
-
- MyStyleHandle
- NewMyStyleHandle(void)
- {
- MyStyleHandle sh;
-
- sh = (MyStyleHandle) NewHandle(sizeof(**sh));
- if(sh == 0)
- return(0);
-
- (*sh)->nRuns = 0;
- (*sh)->nStyles = 0;
- (*sh)->styleTab = (STHandle) NewHandle(0L);
- if((*sh)->styleTab == 0){
- DisposHandle(sh);
- return(0);
- }
- return(sh);
- }
-
- void
- DisposMyStyleHandle(MyStyleHandle sh)
- {
- if(sh){
- if((*sh)->styleTab)
- DisposHandle((*sh)->styleTab);
- (*sh)->styleTab = 0;
- DisposHandle(sh);
- }
- }
-
- /*
- * convert one of my style handles into a TextEdit TEStyleHandle.
- * since my style handles can deal with > 32K of text, *start
- * indicates where to start converting. this routine leaves the
- * first unconverted character position in *start when it returns.
- * it tries not to split lines. it modifies an existing style handle.
- * max specifies how many characters can be put in a field.
- * txt is the total txt (ie might be > 32K).
- * remember that (*sh)->nRuns doesn't count that last dummy run.
- */
- OSErr
- CvtMyStyleHandle(MyStyleHandle sh1, long *start, TEStyleHandle sh2, long max,
- Handle txt)
- {
- STHandle st;
- long firstrun, lastrun, run, txtlen, len, i;
- OSErr err;
-
- txtlen = GetHandleSize(txt);
- if(*start + max > txtlen)
- max = txtlen - *start;
-
- /* shrink max soas to break at a return */
- if(*start + max < txtlen){
- for(i = max-1 ; i >= 0; --i)
- if((*txt)[*start + i] == '\r')
- break;
- if(i > 0)
- max = i + 1;
- }
-
- /* find the first relevant run */
- for(run = 0; run < (*sh1)->nRuns; run++)
- if((*sh1)->runs[run].startChar <= *start &&
- (*sh1)->runs[run + 1].startChar > *start)
- break;
- firstrun = run;
-
- /* find the last relevant run: it must be within max of *start */
- for(run = firstrun; run <= (*sh1)->nRuns; run++)
- if((*sh1)->runs[run].startChar >= *start + max)
- break;
- lastrun = run - 1;
-
- /*
- * allocate enough space in sh2's tables.
- * also copies sh1's styleTab. this might be a bit
- * wasteful if any are unused, but it means that
- * the same indices can be used in sh2 as in sh1.
- */
- (*sh2)->nRuns = lastrun - firstrun + 1;
- (*sh2)->nStyles = (*sh1)->nStyles;
- st = (*sh1)->styleTab;
- HandToHand(&st);
- (*sh2)->styleTab = st;
- SetHandleSize(sh2, 32L + ((*sh2)->nRuns + 1) * sizeof(StyleRun));
- if((err = MemError()) != 0)
- return(err);
-
- /* copy the relevant runs, adjusting the character pointers */
- for(run = firstrun; run <= lastrun; run++){
- (*sh2)->runs[run - firstrun].startChar = (*sh1)->runs[run].startChar - *start;
- if((*sh2)->runs[run - firstrun].startChar < 0)
- (*sh2)->runs[run - firstrun].startChar = 0;
- (*sh2)->runs[run - firstrun].styleIndex = (*sh1)->runs[run].styleIndex;
- }
-
- if(lastrun < (*sh1)->nRuns)
- len = (*sh1)->runs[lastrun + 1].startChar - *start;
- else
- len = txtlen - *start;
- if(len > max)
- len = max;
- (*sh2)->runs[(*sh2)->nRuns].startChar = len;
- *start += len;
-
- return(0);
- }